import {gl, initializeGL, initShaders} from '../common/webgl';
import {ExampleBase} from "../common/example";
import {degreeToRaian, radianToDegree} from '../common/utils';
import {mat4, vec3} from 'gl-matrix';

const vertexCode = `
attribute vec4 a_position;
attribute vec4 a_color;
uniform mat4 u_modelViewMat;
uniform mat4 u_projectMat;

varying vec4 v_color;
void main() {
    v_color = a_color;
    gl_Position = u_projectMat * u_modelViewMat *  a_position;
}
`
const fragmentCode = `
precision mediump float;
varying vec4 v_color;
void main() {
    gl_FragColor = v_color;
}
`
class Attributes {
    public a_position: number;
    public a_color: number;
    public u_modelMat: WebGLUniformLocation;
    public u_projMat: WebGLUniformLocation;
}
class Buffers {
    public positionColor: WebGLBuffer;
}

export class HelloTriangle implements ExampleBase {
    private program: WebGLProgram;
    private attributes: Attributes;
    private buffers: Buffers;

    private modelMat: mat4;
    private projMat: mat4;
    private squareRotation: number = 0.0;
    private lastTime: number;
    private _speed: number = 30;
    get speed() {
        return this._speed;
    }
    set speed(s: number) {
        this._speed = s;
    }
    public init(canvas:HTMLCanvasElement):void {
        if (!initializeGL(canvas)) {
            return;
        }
        let p = initShaders(gl, vertexCode, fragmentCode);
        if (p != null) {
            this.program = p;
        } else {
            console.log('init shader faield')
            return;
        }
        gl.useProgram(this.program);

        this.attributes = new Attributes;
        this.attributes.a_position = gl.getAttribLocation(this.program, 'a_position');
        this.attributes.a_color = gl.getAttribLocation(this.program, 'a_color');
        let u1 = gl.getUniformLocation(this.program, 'u_modelViewMat');
        if (u1 != null) {
            this.attributes.u_modelMat = u1;
        } else {
            console.log('getUniformLocation u_modelViewMat failed');
            return;
        }
        let u2 = gl.getUniformLocation(this.program, 'u_projectMat');
        if (u2 != null) {
            this.attributes.u_projMat = u2;
        } else {
            console.log('getUniformLocation u_projectMat failed');
            return;
        }
        this.initBuffers();
        this.initMats();
        this.loop();
    }
    public loop() {
        setTimeout(() => {
            this.loop();
        }, 1000 / 60);
        this.draw();
    }
    private initBuffers() {
        const positionBuffer = gl.createBuffer();
        if (null == positionBuffer) {
            return;
        }
        
        gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
        const positionColor = [
            -0.5, -0.5, 0.0, 1.0, 0.0, 0.0, 1.0,
            0.5, -0.5, 0.0, 0.0, 1.0, 0.0, 1.0,
            0.5, 0.5, 0.0, 0.0, 0.0, 1.0, 1.0,
            -0.5, 0.5, 0.0, 1.0, 1.0, 0.0, 1.0
        ];
        gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positionColor), gl.STATIC_DRAW);
        this.buffers = { positionColor: positionBuffer };
    }
    private initMats() {
        const fieldOfView = 45 * Math.PI / 180;
        const aspect = gl.canvas.width / gl.canvas.height;
        const zNear = 0.1;
        const zFar = 100.0;
        this.projMat = mat4.create();
        mat4.perspective(this.projMat, fieldOfView, aspect, zNear, zFar);
        this.modelMat = mat4.create();
        mat4.translate(this.modelMat, this.modelMat, [0, 0, -6.0]);
    }
    public draw() {
        let curTime = Date.now();
        if (this.lastTime) {
            const delta = curTime - this.lastTime;
            const ch = this.speed  / 1000.0 * delta;
            this.squareRotation = (this.squareRotation  + ch) % 360;
        }
        this.lastTime = curTime;
        this.modelMat = mat4.create();        
        mat4.translate(this.modelMat, this.modelMat, [0, 0, -6.0]);
        mat4.rotate(this.modelMat, this.modelMat, degreeToRaian(this.squareRotation), [1,0,1])
        // mat4.translate(this.modelMat, this.modelMat, [this.squareXOffset, this.squareYOffset, this.squareZOffset]);
        
        gl.clearColor(0.2, 0.3, 0.4, 1.0);
        gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
        gl.enable(gl.DEPTH_TEST);
        gl.depthFunc(gl.LEQUAL);

        gl.bindBuffer(gl.ARRAY_BUFFER, this.buffers.positionColor);
        {
            const numComponents = 3;
            const type = gl.FLOAT;
            const normalize = false;
            const stride = 7 * 4;
            const offset = 0;
            gl.vertexAttribPointer(this.attributes.a_position,
                numComponents,
                type,
                normalize,
                stride,
                offset
            );
            gl.enableVertexAttribArray(this.attributes.a_position);
        }
        {
            const numComponents = 4;
            const type = gl.FLOAT;
            const normalize = false;
            const stride = 7 * 4;
            const offset = 3 * 4;
            gl.vertexAttribPointer(this.attributes.a_color,
                numComponents,
                type,
                normalize,
                stride,
                offset
            );
            gl.enableVertexAttribArray(this.attributes.a_color);
        }
        gl.uniformMatrix4fv(this.attributes.u_projMat, false, this.projMat);
        gl.uniformMatrix4fv(this.attributes.u_modelMat, false, this.modelMat);
        {
            const offset = 0;
            const vertexCount = 4;
            gl.drawArrays(gl.TRIANGLE_FAN, offset, vertexCount);
        }
    }

}
